package gov.fnal.mcginnis.threetone;




import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.UIManager;

import com.astrofizzbizz.pixie.PixieImage;
import com.astrofizzbizz.pixie.PixieImageRGBPlotter;
import com.astrofizzbizz.simpleXml.SimpleXmlException;
import com.astrofizzbizz.utilities.FileChooserWrapper;
import com.astrofizzbizz.utilities.simplewebbrowser.SimpleWebBrowserPanel;


/**
 * @author mcginnis
 *
 */
public class ThreeToneGui extends JFrame
{
	protected String version = "v1.4";
	protected String versionDate = "November 5,2008";
	private static final long serialVersionUID = 1L;
	private String lastDirectoryPath = null;
  	static StatusBar statusBar = new StatusBar();
	PixieImageRGBPlotter rGBplotter = new PixieImageRGBPlotter();
 	static Component mainFrameContents = null;
 	protected JPanel settingsAndImagePanel = null;
 	protected JEditorPane helpPane = null;
 	protected JTabbedPane globalSettingsPane = new JTabbedPane();
 	protected int globalSettingsPaneWidth = 300;
	protected JTabbedPane imageTabbedPane = null;    	
	protected JTabbedPane mainTabbedPane = null;    	
	
	JLabel[] imageIconLabel = new JLabel[3];
 	JPanel[] imagePanel = new JPanel[3];
 	int inumLevels = 4;
 	protected JSlider[] singleLevelSlider = new JSlider[4];
    private JSlider[] satSlider = {null, null};
 
	private ImageIcon jpegImageIcon = null;
	protected int imaxNoOfPossibleColors = 56;
	private String[] xpossibleColor = new String[imaxNoOfPossibleColors];
	protected String[] colorButtonString = new String[imaxNoOfPossibleColors];
	private JButton[] colorButton = new JButton[imaxNoOfPossibleColors];
	private ThreeToneGuiListeners[] buttonListener = new ThreeToneGuiListeners[imaxNoOfPossibleColors];
	private int maxNoOfMixColors = 4;
	private JButton[] colorAcceptButton = new JButton[maxNoOfMixColors];
	private int[] colorMixSelection = new int[maxNoOfMixColors];
	JSlider[] colorMixValueSlider = new JSlider[maxNoOfMixColors];
	private int inumColorPicked = -1;
	private int inullColorBox = imaxNoOfPossibleColors - 1;
	protected boolean colorMatchingOn = true;
	protected int	imatchColor = 0;
	BufferedImageWrapper bSourceImage;
	private int ixswatchSize = 100;
	private int iyswatchSize = 200;
	private int colorBoxSize = 15;
	private JLabel[] swatchColorLabel = new JLabel[2];
	int 	itargetcolor;
	boolean validTargetColor = false;
	private JLabel thumbnailLabel = null;
	protected int ngrid  = 1;
	protected int[] saturationChange = {100, 100};
	protected JComboBox gridSetupComboBox = null;
	protected JComboBox aspectSetupComboBox = null;
	protected JComboBox orientSetupComboBox = null;
	protected JComboBox flipSetupComboBox = null;
	protected JComboBox noLevelComboBox = null;
	protected JTextField aspectWidthTextField = new JTextField(5);
	protected JTextField aspectHeightTextField = new JTextField(5);
	protected boolean flipSetting = false;
	protected double aspectRatio = 1.333;
	protected int[][]   icroppedCorners = null;
	protected int[]     origin = null;
	protected double    dimagediagonalLength = 1.0;
	protected double aspectWidth = 12.0;
	protected double aspectHeight = 9.0;

	protected int maxNoLines = 3;
	protected boolean lineEnter = false;
	protected boolean lineEnterFirstPt = false;
	protected int[][] lineCoords = new int[2][2];
	protected boolean[] validLine = {false,false,false,false,false};
	protected int lineIndex = -1;
	JCheckBox[]  validLineCheckBox = new JCheckBox[4];
	protected double unitLineLength = -1.0;
	private int maxNoOfExtensionLines = 10;
	private int[][][] extensionLine = new int[maxNoOfExtensionLines][2][2];
	private int noOfExtensionLines = 0;
	private int[][][] unitLine = new int[1][2][2];
	private int noOfUnitLines = 0;
	private int[][][] measureLine = new int[1][2][2];
	private int noOfMeasureLines = 0;
	private int[][][] originLine = new int[2][2][2];
	private int[][][] cursorLine = new int[2][2][2];
	private boolean validCursor = false;
	private int noOfOriginLines = 0;
	private DecimalFormat twoPlaces = new DecimalFormat("0.00");
	protected boolean autoLevelReset = true;
	protected int[] xScaleTablebreaks = null;
	protected boolean ignoreLevelChange = true;


	/**
	 * @throws SimpleXmlException 
	 * 
	 */
	public ThreeToneGui() throws SimpleXmlException
	{
		for (int ii = 0; ii < imaxNoOfPossibleColors; ++ii)
		{
			xpossibleColor[ii] = "C8C8C8";
			colorButtonString[ii] = "";
		}

		xpossibleColor[0] = "000000";
		xpossibleColor[1] = "FFFFFF";
		xpossibleColor[2] = "BE0303";
		xpossibleColor[3] = "C00202";
		xpossibleColor[4] = "75100B";
		xpossibleColor[5] = "B12D00";
		xpossibleColor[6] = "E84B00";
		xpossibleColor[7] = "3C2510";
		xpossibleColor[8] = "C36E00";
		xpossibleColor[9] = "EF8C00";
		xpossibleColor[10] = "EF8D02";
		xpossibleColor[11] = "F4A50A";
		xpossibleColor[12] = "F2AA00";
		xpossibleColor[13] = "E2AA05";
		xpossibleColor[14] = "FCC119";
		xpossibleColor[15] = "F9DB60";
		xpossibleColor[16] = "FCD31C";
		xpossibleColor[17] = "413E2F";
		xpossibleColor[18] = "F9F98E";
		xpossibleColor[19] = "F2F902";
		xpossibleColor[20] = "F7FC35";
		xpossibleColor[21] = "3CE000";
		xpossibleColor[22] = "1B7A00";
		xpossibleColor[23] = "158100";
		xpossibleColor[24] = "32DE19";
		xpossibleColor[25] = "24AD22";
		xpossibleColor[26] = "A3F2A3";
		xpossibleColor[27] = "177320";
		xpossibleColor[28] = "123524";
		xpossibleColor[29] = "54D1D8";
		xpossibleColor[30] = "3FBAFF";
		xpossibleColor[31] = "151D24";
		xpossibleColor[32] = "1970FC";
		xpossibleColor[33] = "002CD6";
		xpossibleColor[34] = "000F89";
		xpossibleColor[35] = "000E9A";
		xpossibleColor[36] = "0004BD";
		xpossibleColor[37] = "130FA4";
		xpossibleColor[38] = "10009B";
		xpossibleColor[39] = "1300A5";
		xpossibleColor[40] = "0C005A";
		xpossibleColor[41] = "7100C1";
		xpossibleColor[42] = "53124C";
		xpossibleColor[43] = "241420";
		xpossibleColor[44] = "ED70B5";
		xpossibleColor[45] = "3E0423";
		xpossibleColor[46] = "E30574";
		xpossibleColor[47] = "DE0452";
		xpossibleColor[48] = "970015";
		xpossibleColor[49] = "E30022";
		xpossibleColor[50] = "BE0019";
		xpossibleColor[51] = "DE0009";
		xpossibleColor[52] = "9C0004";

		colorButtonString[0]  = "Mars Black    ";
		colorButtonString[1]  = "Titanium White";
		colorButtonString[2]  = "Ind Red       ";
		colorButtonString[3]  = "Pyr Red       ";
		colorButtonString[4]  = "Ven Red       ";
		colorButtonString[5]  = "Bnt Sienna    ";
		colorButtonString[6]  = "Cad Orange    ";
		colorButtonString[7]  = "Raw Umber     ";
		colorButtonString[8]  = "Raw Sienna    ";
		colorButtonString[9]  = "Cad Yellow    ";
		colorButtonString[10] = "Han Yellow Dp ";
		colorButtonString[11] = "Cad Yellow Dp ";
		colorButtonString[12] = "Ind Yellow    ";
		colorButtonString[13] = "Yellow Ochre  ";
		colorButtonString[14] = "Cad Yellow Lt ";
		colorButtonString[15] = "Nap Yellow    ";
		colorButtonString[16] = "Han Yellow    ";
		colorButtonString[17] = "VanD Brown    ";
		colorButtonString[18] = "Nap Yellow Lt ";
		colorButtonString[19] = "Win Yellow    ";
		colorButtonString[20] = "Han Yellow Lt ";
		colorButtonString[21] = "Cad Green     ";
		colorButtonString[22] = "Sap Green     ";
		colorButtonString[23] = "Prus Green    ";
		colorButtonString[24] = "Perm Green Lt ";
		colorButtonString[25] = "Viridian      ";
		colorButtonString[26] = "Cob Green     ";
		colorButtonString[27] = "Perm Green    ";
		colorButtonString[28] = "Pth Green     ";
		colorButtonString[29] = "Cer Blue      ";
		colorButtonString[30] = "Mag Blue      ";
		colorButtonString[31] = "Pay Gray      ";
		colorButtonString[32] = "Cob Blue      ";
		colorButtonString[33] = "French U Blue ";
		colorButtonString[34] = "Pth Blue      ";
		colorButtonString[35] = "Prus Blue     ";
		colorButtonString[36] = "Cob Blue Dp   ";
		colorButtonString[37] = "Ult Blue      ";
		colorButtonString[38] = "Dio Purple    ";
		colorButtonString[39] = "Cob Violet Dk ";
		colorButtonString[40] = "Perm Mauve    ";
		colorButtonString[41] = "Cob Violet    ";
		colorButtonString[42] = "Quin Maroon   ";
		colorButtonString[43] = "Bnt Umber     ";
		colorButtonString[44] = "Ros Mad Dp    ";
		colorButtonString[45] = "Purple Mad Alz";
		colorButtonString[46] = "Ros Mad Gen   ";
		colorButtonString[47] = "Quin Red      ";
		colorButtonString[48] = "Cad Red Dp    ";
		colorButtonString[49] = "Cad Red       ";
		colorButtonString[50] = "Carmine       ";
		colorButtonString[51] = "Win Red       ";
		colorButtonString[52] = "Alz Crimson   ";

		colorButtonString[imaxNoOfPossibleColors - 1] = "Clear";

		for (int ii = 0; ii < maxNoOfMixColors; ++ii)
		{
			colorMixSelection[ii] = -1;
		}
		return;
	}

	private Component createComponents() 
	{
		setupImageDisplayPanel();
	 	
		settingsAndImagePanel = new JPanel();
		imageTabbedPane = new JTabbedPane();
	 	imageTabbedPane.addTab("Color Image", imagePanel[0]);
	 	imageTabbedPane.addTab("BW Image", imagePanel[1]);
	 	imageTabbedPane.addTab("Single Color Image", imagePanel[2]);
	 	imageTabbedPane.addChangeListener(new ThreeToneGuiListeners(this, "imagePaneChanged"));

	 	
        settingsAndImagePanel.setBorder(BorderFactory.createEmptyBorder(1,3,5,5));
        settingsAndImagePanel.setLayout(new BoxLayout(settingsAndImagePanel, BoxLayout.X_AXIS));
 
        globalSettingsPane.addTab("Layout", setupLayoutPanel());
        globalSettingsPane.addTab("Levels", setupLevelSettingsPanel());
        globalSettingsPane.setMinimumSize(new Dimension(globalSettingsPaneWidth,1));
        globalSettingsPane.setSize(new Dimension(globalSettingsPaneWidth,1));
        globalSettingsPane.setPreferredSize(new Dimension(globalSettingsPaneWidth,1));
        
        settingsAndImagePanel.add(globalSettingsPane);
        settingsAndImagePanel.add(imageTabbedPane);
		imageTabbedPane.setSelectedIndex(1);
		statusBar.setMessage("No File");
		
		JPanel mainPanel = new JPanel();
		mainPanel.setBorder(BorderFactory.createEmptyBorder(1,3,5,5));
		mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.X_AXIS));
		mainTabbedPane = new JTabbedPane();
	 	mainTabbedPane.addTab("Image", settingsAndImagePanel);
	 	mainTabbedPane.addTab("Color Picker", setupColorPickerPanel());
	 	
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		URL resourceURL = loader.getResource("help/tones.html");
		SimpleWebBrowserPanel swbp = new SimpleWebBrowserPanel(resourceURL.toString(), this);
		mainTabbedPane.addTab("Help", swbp);
		
		mainTabbedPane.addChangeListener(new ThreeToneGuiListeners(this, "MainTabbedChanged"));
	 	
	 	mainPanel.add(mainTabbedPane);
		ThreeToneGuiListeners listener  = new ThreeToneGuiListeners(this, "ReSize");
		mainPanel.addComponentListener(listener);
                
        return mainPanel;
	}
	protected void customColorPalette() 
	{
		FileChooserWrapper fc = new FileChooserWrapper(this);
		File paletteFile = fc.fOpenFile("xml",false, lastDirectoryPath);
		if (paletteFile == null) return;
     	lastDirectoryPath = paletteFile.getPath();

     	ThreeTonePalette palette = null;
		try {
			palette  = new ThreeTonePalette(paletteFile.toURI().toURL());
		} catch (SimpleXmlException e) {
			e.printStackTrace();
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		for (int ii = 0; ii < imaxNoOfPossibleColors; ++ii)
		{
			xpossibleColor[ii] = "C8C8C8";
			colorButtonString[ii] = "";
			colorButton[ii].removeActionListener(buttonListener[ii]);
		}
		int numcolors = palette.numColors();
		if (numcolors >= imaxNoOfPossibleColors) numcolors = imaxNoOfPossibleColors - 1;
 		for (int ii = 0; ii < numcolors; ++ii)
		{
			colorButtonString[ii] = palette.paletteColor[ii].title;
			xpossibleColor[ii] = palette.paletteColor[ii].rgbHex;
		}
		colorButtonString[imaxNoOfPossibleColors - 1] = "Clear";
		for (int ii = 0; ii < imaxNoOfPossibleColors; ++ii)
		{
			colorButton[ii].setText(colorButtonString[ii]);
			colorButton[ii].setIcon(new ColorBox(xpossibleColor[ii],colorBoxSize,colorBoxSize, false));
			buttonListener[ii] = new ThreeToneGuiListeners(this, colorButtonString[ii]);
			colorButton[ii].addActionListener(buttonListener[ii]);
		}
	}
	protected void loadSplashScreen()
	{
// following code used to get image from jar files so it will be included in fat jar
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		URL resourceURL = loader.getResource("tommyTruck.jpg");
		Toolkit tk = this.getToolkit();
		Image tommyTruck = tk.getImage(resourceURL);
		jpegImageIcon = new ImageIcon(tommyTruck);
		resizeMainPanels();
     	plotImage();
	}
	protected void resizeMainPanels()
	{
//		System.out.println("Resizing Image");
		int settingsAndImagePanelwidth = settingsAndImagePanel.getWidth();
		int settingsAndImagePanelheight = settingsAndImagePanel.getHeight();
		int settingsPanelwidth = globalSettingsPane.getWidth();
		if (!globalSettingsPane.isVisible()) 
		{
			settingsPanelwidth = 0;
		}
		else
		{
			settingsPanelwidth = globalSettingsPaneWidth;
		}
		double tabbedPanelWidth = 1.00 * ((double) (settingsAndImagePanelwidth - settingsPanelwidth));
		int itabbedPanelWidth = (int) tabbedPanelWidth - 10;
		int itabbedPanelHeight = settingsAndImagePanelheight - 10;
		imageTabbedPane.setSize(new Dimension(itabbedPanelWidth, itabbedPanelHeight));
		imageTabbedPane.setPreferredSize(new Dimension(itabbedPanelWidth, itabbedPanelHeight));
		
//		System.out.println("Desired = " + itabbedPanelWidth + " Read = " + mainTabbedPane.getSize().getWidth());
	}
	JPanel setupLevelSettingsPanel()
	{

		ThreeToneGuiListeners[] threeToneGuiListener = new ThreeToneGuiListeners[6];
		threeToneGuiListener[0] = new ThreeToneGuiListeners(this, "Grey Linear");
		threeToneGuiListener[1] = new ThreeToneGuiListeners(this, "Grey Log");
		threeToneGuiListener[2] = new ThreeToneGuiListeners(this, "Grey Hist Eq");
		threeToneGuiListener[3] = new ThreeToneGuiListeners(this, "Grey Invert Image");
		threeToneGuiListener[4] = new ThreeToneGuiListeners(this, "Grey Auto Scale");
		threeToneGuiListener[5] = new ThreeToneGuiListeners(this, "Grey Color Spectrum");
		rGBplotter.setScaleType("log");
		rGBplotter.setInvertImage(false);
		rGBplotter.setAutoScale(true);
		rGBplotter.setColorSpectrum(false);
		rGBplotter.setPixelValueLimits(0.0, 256.0);
		JPanel graphicsTypePanel = rGBplotter.pngGraphicsTypePanel("Grey Scale", threeToneGuiListener);

	 	JPanel levelSettingsPanel = new JPanel();
		levelSettingsPanel.setLayout(new BoxLayout(levelSettingsPanel, BoxLayout.PAGE_AXIS));
		levelSettingsPanel.add(graphicsTypePanel);
		
	 	JPanel bottomPanel = new JPanel();
	 	bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.PAGE_AXIS));
	 	bottomPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Level Control"),BorderFactory.createEmptyBorder(5,5,5,5)));
		levelSettingsPanel.add(bottomPanel);

		JPanel numLevelPanel = new JPanel();
		numLevelPanel.setLayout(new BoxLayout(numLevelPanel, BoxLayout.Y_AXIS));
		numLevelPanel.add(new JLabel("Number of Levels"));
		String[] snoLevels = {"1","2","3","4","5"};
	    noLevelComboBox = new JComboBox(snoLevels);
	    noLevelComboBox.setSize(new Dimension(10,10));
	    noLevelComboBox.setSelectedIndex(inumLevels - 1);
	    ThreeToneGuiListeners noLevelComboBoxListener = new ThreeToneGuiListeners(this,"Number of Levels");
	    noLevelComboBox.addActionListener(noLevelComboBoxListener);
		JPanel noLevelComboBoxPanel = new JPanel();
		noLevelComboBoxPanel.add(noLevelComboBox);
	    bottomPanel.add(numLevelPanel);
	    bottomPanel.add(noLevelComboBoxPanel);
	    bottomPanel.add(levelSliderSetup());
	    

	    return levelSettingsPanel;
	}
	JPanel setupLayoutPanel()
	{
	 	JPanel layoutPanel = new JPanel();
	 	layoutPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Layout Control"),BorderFactory.createEmptyBorder(5,5,5,5)));
	 	layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS));
	 	
	 	JPanel gridSetupPanel = new JPanel();
	 	gridSetupPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Grid"),BorderFactory.createEmptyBorder(5,5,5,5)));
	    String[] sGridTypes = {"1x1","2x2","3x3","4x4"};
	    gridSetupComboBox = new JComboBox(sGridTypes);
	    gridSetupComboBox.setSelectedIndex(0);
	    gridSetupPanel.add(gridSetupComboBox);
	    ThreeToneGuiListeners gridSetupComboBoxListener = new ThreeToneGuiListeners(this,"gridSetup");
	    gridSetupComboBox.addActionListener(gridSetupComboBoxListener);
	    layoutPanel.add(gridSetupPanel);

	 	JPanel aspectSetupPanel = new JPanel();
	 	aspectSetupPanel.setLayout(new FlowLayout());
	 	aspectSetupPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Aspect Ratio"),BorderFactory.createEmptyBorder(5,5,5,5)));
	    String[] sAspectTypes = {"1x1","8x10","9x12","11x14","Custom"};
	    aspectSetupComboBox = new JComboBox(sAspectTypes);
	    aspectSetupComboBox.setSelectedIndex(2);
	    aspectSetupPanel.add(aspectSetupComboBox);
	    ThreeToneGuiListeners aspectSetupComboBoxListener = new ThreeToneGuiListeners(this,"aspectSetup");
	    aspectSetupComboBox.addActionListener(aspectSetupComboBoxListener);
	    
	    JPanel aspectDimensions = new JPanel();
	    aspectDimensions.setLayout(new GridLayout(2,2,1,1));
	    aspectWidthTextField.setHorizontalAlignment(JTextField.CENTER);
	    aspectHeightTextField.setHorizontalAlignment(JTextField.CENTER);
	    aspectWidthTextField.setText(twoPlaces.format(aspectWidth));
	    aspectHeightTextField.setText(twoPlaces.format(aspectHeight));
		aspectRatio = aspectWidth / aspectHeight;
		dimagediagonalLength = Math.sqrt(aspectWidth * aspectWidth + aspectHeight * aspectHeight);
	    aspectDimensions.add(aspectWidthTextField);
	    aspectDimensions.add(new JLabel("W"));
	    aspectDimensions.add(aspectHeightTextField);
	    aspectDimensions.add(new JLabel("H"));
	    ThreeToneGuiListeners aspectTextFieldListener = new ThreeToneGuiListeners(this,"aspectTextField");
	    aspectWidthTextField.addActionListener(aspectTextFieldListener);
	    aspectHeightTextField.addActionListener(aspectTextFieldListener);
	    aspectSetupPanel.add(aspectDimensions);
	    
	    layoutPanel.add(aspectSetupPanel);

	    JPanel orientSetupPanel = new JPanel();
	 	orientSetupPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Orientation"),BorderFactory.createEmptyBorder(5,5,5,5)));
	    String[] sOrientTypes = {"Landscape","Portrait"};
	    orientSetupComboBox = new JComboBox(sOrientTypes);
	    orientSetupComboBox.setSelectedIndex(0);
	    orientSetupPanel.add(orientSetupComboBox);
	    ThreeToneGuiListeners orientSetupComboBoxListener = new ThreeToneGuiListeners(this,"orientSetup");
	    orientSetupComboBox.addActionListener(orientSetupComboBoxListener);
	    layoutPanel.add(orientSetupPanel);

	 	JPanel flipSetupPanel = new JPanel();
	 	flipSetupPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Flip"),BorderFactory.createEmptyBorder(5,5,5,5)));
	    String[] sflipTypes = {"Normal","Flip"};
	    flipSetupComboBox = new JComboBox(sflipTypes);
	    flipSetupComboBox.setSelectedIndex(0);
	    flipSetupPanel.add(flipSetupComboBox);
	    ThreeToneGuiListeners flipSetupComboBoxListener = new ThreeToneGuiListeners(this,"flipSetup");
	    flipSetupComboBox.addActionListener(flipSetupComboBoxListener);
	    flipSetting = false;
	    layoutPanel.add(flipSetupPanel);
	    
	 	JPanel linesSetupPanel = new JPanel();
	 	linesSetupPanel.setLayout(new GridLayout(2,2,1,1));
	 	linesSetupPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Lines"),BorderFactory.createEmptyBorder(5,5,5,5)));
	 	ThreeToneGuiListeners[] validLineCBListen = new ThreeToneGuiListeners[4];
	 	validLineCheckBox[0] = new JCheckBox("Extend");
	 	validLineCBListen[0] = new ThreeToneGuiListeners(this,"Line Check Box");
	 	validLineCheckBox[0].addActionListener(validLineCBListen[0]);
	 	validLineCheckBox[1] = new JCheckBox("Unit");
	 	validLineCBListen[1] = new ThreeToneGuiListeners(this,"Line Check Box");
	 	validLineCheckBox[1].addActionListener(validLineCBListen[1]);
	 	validLineCheckBox[2] = new JCheckBox("Measure");
	 	validLineCBListen[2] = new ThreeToneGuiListeners(this,"Line Check Box");
	 	validLineCheckBox[2].addActionListener(validLineCBListen[2]);
	 	validLineCheckBox[3] = new JCheckBox("Origin");
	 	validLineCBListen[3] = new ThreeToneGuiListeners(this,"Line Check Box");
	 	validLineCheckBox[3].addActionListener(validLineCBListen[3]);
	 	linesSetupPanel.add(validLineCheckBox[0]);
	 	linesSetupPanel.add(validLineCheckBox[1]);
	 	linesSetupPanel.add(validLineCheckBox[2]);
	 	linesSetupPanel.add(validLineCheckBox[3]);
	 	layoutPanel.add(linesSetupPanel);

	 	JPanel bottomPanel = new JPanel();
	 	bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
	 	layoutPanel.add(bottomPanel);
        
		JPanel satPanel = new JPanel();
		JPanel[] satSliderPanel = {new JPanel(),new JPanel()};
		satPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Saturation"),BorderFactory.createEmptyBorder(5,5,5,5)));
		satPanel.setLayout(new GridLayout(2,1,5,5));
	 	for (int ii = 0; ii < 2; ++ii)
	 	{
		 	satSlider[ii] = new JSlider(JSlider.HORIZONTAL, 0, 100, saturationChange[ii]);
			satSlider[ii].addChangeListener(new ThreeToneGuiListeners(this, "Sat Slider Changed"));
	
	//		Turn on labels at major tick marks.
			satSlider[ii].setMajorTickSpacing(25);
			satSlider[ii].setMinorTickSpacing(25);
			satSlider[ii].setPaintTicks(true);
			satSlider[ii].setPaintLabels(true);
			satSlider[ii].setSnapToTicks(false);

	        if (ii == 0) satSliderPanel[ii].add(new JLabel("White"));
	        if (ii == 1) satSliderPanel[ii].add(new JLabel("Black"));
	        satSliderPanel[ii].add(satSlider[ii]);
		 	satPanel.add(satSliderPanel[ii]);
	 	}
       layoutPanel.add(satPanel);
	 	
	 	
	 	return layoutPanel;
	}
	protected void readLayoutSetup()
	{
		int iaspect = aspectSetupComboBox.getSelectedIndex();
		int iorient = orientSetupComboBox.getSelectedIndex();
		int iflip = flipSetupComboBox.getSelectedIndex();
		
		if (iaspect == 0) 
		{
			aspectWidth = 1.0;
			aspectHeight = 1.0;
		}
		if (iaspect == 1)
		{
			aspectWidth = 10.0;
			aspectHeight = 8.0;
		}
		if (iaspect == 2) 
		{
			aspectWidth = 12.0;
			aspectHeight = 9.0;
		}
		if (iaspect == 3)
		{
			aspectWidth = 14.0;
			aspectHeight = 11.0;
		}
		if ((iorient == 1) && (iaspect < 4))
		{
			double temp = aspectWidth;
			aspectWidth = aspectHeight;
			aspectHeight = temp;
		}
		if (iaspect < 4) 
		{
			aspectWidthTextField.setText(twoPlaces.format(aspectWidth));
			aspectHeightTextField.setText(twoPlaces.format(aspectHeight));
		}
		if (iaspect == 4)
		{
			aspectWidth = Double.parseDouble(aspectWidthTextField.getText());
			aspectHeight = Double.parseDouble(aspectHeightTextField.getText());
		}
		aspectRatio = aspectWidth / aspectHeight;
		dimagediagonalLength = Math.sqrt(aspectWidth * aspectWidth + aspectHeight * aspectHeight);
		if (iflip == 0) flipSetting = false;
		if (iflip == 1) flipSetting = true;
		resizeMainPanels();
		plotImage();
	}
	JPanel setupColorPickerPanel()
	{

		JPanel colorPickerPanel = new JPanel();
		colorPickerPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Possible Colors"),BorderFactory.createEmptyBorder(5,5,5,5)));
		colorPickerPanel.setLayout(new GridLayout(8,7,5,5));

		
		for (int ii = 0; ii < imaxNoOfPossibleColors; ++ii)
		{
			colorButton[ii] = new JButton(colorButtonString[ii], new ColorBox(xpossibleColor[ii],colorBoxSize,colorBoxSize, false));
			colorButton[ii].setHorizontalAlignment(SwingConstants.LEFT);
			colorButton[ii].setHorizontalTextPosition(SwingConstants.RIGHT);
//			colorButton[ii].setSize(40, 20);
			colorPickerPanel.add(colorButton[ii]);
			buttonListener[ii] = new ThreeToneGuiListeners(this, colorButtonString[ii]);
			colorButton[ii].addActionListener(buttonListener[ii]);
		}

		JPanel mixturePanel = new JPanel();
		mixturePanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Selected Colors"),BorderFactory.createEmptyBorder(5,5,5,5)));
		mixturePanel.setLayout(new BoxLayout(mixturePanel, BoxLayout.X_AXIS));
		ThreeToneGuiListeners[] acceptButtonListener = new ThreeToneGuiListeners[maxNoOfMixColors];
		JPanel[] colorMixValuePanel = new JPanel[maxNoOfMixColors];
		ThreeToneGuiListeners[] colorSliderListen = new ThreeToneGuiListeners[maxNoOfMixColors];
		for (int ii = 0; ii < maxNoOfMixColors; ++ii)
		{
			colorAcceptButton[ii] = new JButton(colorButtonString[inullColorBox],new ColorBox(xpossibleColor[inullColorBox],colorBoxSize,colorBoxSize, false));
			colorMixValuePanel[ii] = new JPanel();
			colorMixValuePanel[ii].setLayout(new BoxLayout(colorMixValuePanel[ii], BoxLayout.Y_AXIS));

			colorMixValueSlider[ii] = new JSlider(JSlider.VERTICAL, 0, 100, 0);
			colorSliderListen[ii] = new ThreeToneGuiListeners(this, "Color Slider Changed");
			colorMixValueSlider[ii].addChangeListener(colorSliderListen[ii]);

//			Turn on labels at major tick marks.
			colorMixValueSlider[ii].setMajorTickSpacing(25);
			colorMixValueSlider[ii].setMinorTickSpacing(25);
			colorMixValueSlider[ii].setPaintTicks(true);
			colorMixValueSlider[ii].setPaintLabels(true);
			colorMixValueSlider[ii].setSnapToTicks(false);
			
			
			colorMixValuePanel[ii].add(colorAcceptButton[ii]);
			colorMixValuePanel[ii].add(colorMixValueSlider[ii]);
			mixturePanel.add(colorMixValuePanel[ii]);

			String smessage = "Color Accept " + ii;
			acceptButtonListener[ii] = new ThreeToneGuiListeners(this,smessage);
			colorAcceptButton[ii].addActionListener(acceptButtonListener[ii]);
		}
		JPanel mixMatchControlPanel = new JPanel();
		mixMatchControlPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Mix-Match Control"),BorderFactory.createEmptyBorder(5,5,5,5)));
		mixMatchControlPanel.setLayout(new FlowLayout());
		JRadioButton matchButton = new JRadioButton("Match");
		matchButton.setSelected(colorMatchingOn);
		JRadioButton mixButton = new JRadioButton("Mix");
		mixButton.setSelected(!colorMatchingOn);
		ButtonGroup mixMatchGroup = new ButtonGroup();
		mixMatchGroup.add(matchButton);
		mixMatchGroup.add(mixButton);
		mixMatchControlPanel.add(matchButton);
		mixMatchControlPanel.add(mixButton);
		ThreeToneGuiListeners matchButtonListen = new ThreeToneGuiListeners(this,"Color Match");
		ThreeToneGuiListeners mixButtonListen = new ThreeToneGuiListeners(this,"Color Mix");
		matchButton.addActionListener(matchButtonListen);
		mixButton.addActionListener(mixButtonListen);
	    
		
		JPanel swatchPanel = new JPanel();
		swatchPanel.setLayout(new BoxLayout(swatchPanel, BoxLayout.PAGE_AXIS));
		swatchColorLabel[0] = new JLabel("Desired", new ColorBox(200,200,200,ixswatchSize,iyswatchSize, true),SwingConstants.CENTER);
		swatchColorLabel[1] = new JLabel("Mixed  ", new ColorBox(200,200,200,ixswatchSize,iyswatchSize, true),SwingConstants.CENTER);
		swatchColorLabel[0].setForeground(Color.WHITE);
		swatchColorLabel[0].setHorizontalAlignment(SwingConstants.LEFT);
		swatchColorLabel[0].setHorizontalTextPosition(SwingConstants.RIGHT);
		swatchColorLabel[1].setForeground(Color.WHITE);
		swatchPanel.add(mixMatchControlPanel);
		swatchPanel.add(swatchColorLabel[0]);
		swatchPanel.add(swatchColorLabel[1]);
		swatchPanel.setBackground(Color.BLACK);
				
		JPanel thumbnailPanel = new JPanel();
		thumbnailPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Thumbnail - Click to return"),BorderFactory.createEmptyBorder(5,5,5,5)));
		thumbnailLabel = new JLabel();
		ThreeToneGuiListeners thumbnailLabelListen = new ThreeToneGuiListeners(this,"Thumbnail Clicked");
		thumbnailLabel.addMouseListener(thumbnailLabelListen);

		thumbnailPanel.add(thumbnailLabel);
		
		JPanel bottomPanel = new JPanel();
		bottomPanel.setLayout(new BoxLayout(bottomPanel, BoxLayout.X_AXIS));
		bottomPanel.add(thumbnailPanel);
		bottomPanel.add(mixturePanel);
		bottomPanel.add(swatchPanel);

		JPanel mainColorPickerPanel = new JPanel();
		mainColorPickerPanel.setBorder(BorderFactory.createEmptyBorder(1,3,5,5));
//		mainColorPickerPanel.setLayout(new GridLayout(4,1,5,5));
		mainColorPickerPanel.setLayout(new BoxLayout(mainColorPickerPanel, BoxLayout.PAGE_AXIS));
		mainColorPickerPanel.setBackground(Color.BLACK);
		mainColorPickerPanel.add(colorPickerPanel);
//		mainColorPickerPanel.add(mixturePanel);
		mainColorPickerPanel.add(bottomPanel);
		return mainColorPickerPanel;
	}
	protected void colorButtonSelected(int ibutton)
	{
		if (inumColorPicked >= 0)
		{
			colorButton[inumColorPicked].setIcon(new ColorBox(xpossibleColor[inumColorPicked],colorBoxSize,colorBoxSize, false));
		}
		inumColorPicked = ibutton;
		colorButton[inumColorPicked].setIcon(new ColorBox(xpossibleColor[inumColorPicked],colorBoxSize,colorBoxSize, true));
	}
	protected void colorAccepted(int ibutton)
	{
		if (inumColorPicked < 0) return;
		colorAcceptButton[ibutton].setIcon(new ColorBox(xpossibleColor[inumColorPicked],colorBoxSize,colorBoxSize, false));
		colorAcceptButton[ibutton].setText(colorButtonString[inumColorPicked]);
		colorMixValueSlider[ibutton].setValue(0);
		colorButton[inumColorPicked].setIcon(new ColorBox(xpossibleColor[inumColorPicked],colorBoxSize,colorBoxSize, false));
		if (inumColorPicked == (imaxNoOfPossibleColors - 1)) inumColorPicked = -1;
		colorMixSelection[ibutton] = inumColorPicked;
		inumColorPicked = -1;
		if (colorMatchingOn) matchColors(true);
	}
	protected void mouseClickedOnColorImage(MouseEvent e)
	{
		int ix = e.getX();
		int iy = e.getY();
//		itargetcolor = bSourceImage.getRGB(ix, iy);
		itargetcolor = bSourceImage.getAveragePixelColor(ix,iy,1);
		validTargetColor = true;
		Color	clr  = new Color(itargetcolor);
		int ired = clr.getRed();
		int igreen = clr.getGreen();
		int iblue = clr.getBlue();
		float[] hsbvals = new float[3];
		Color.RGBtoHSB(ired, igreen, iblue, hsbvals);
//		System.out.println("H = " + hsbvals[0] + " S= " + hsbvals[1] + " V= " + hsbvals[2]);
//		System.out.println("X = " + ix + " Y = " + iy);
//		System.out.println("R = " + ired + " G = " + igreen + " B = " + iblue);
		swatchColorLabel[0].setIcon(new ColorBox(itargetcolor,ixswatchSize,iyswatchSize, true));
		matchColors(true);
		BufferedImageWrapper zoomedImage = bSourceImage.getZoomedImage(ix, iy, 2 * ixswatchSize, 2 * ixswatchSize);
		zoomedImage.drawGridonImage((Color.BLACK).getRGB(), 2, 2, 1, -1);

		ImageIcon imageIcon = new ImageIcon(zoomedImage);
		thumbnailLabel.setIcon(imageIcon);
		mainTabbedPane.setSelectedIndex(1);
	}
	protected void mouseClickedOnBWImage(MouseEvent e)
	{
		int ix = e.getX();
		int iy = e.getY();
		if (!lineEnter) 
		{
			double dx = (double) (ix - origin[0]);
			double dy = (double) (iy - origin[1]);
			double dscale = 1.0;
			if (unitLineLength > 0.0)
			{
				dscale = 100.0 / unitLineLength;
				dx = dx * dscale;
				dy = -dy * dscale;	
				statusBar.setMessage("DX = " + twoPlaces.format(dx) + "% DY = " + twoPlaces.format(dy) + "%");
			}
			else
			{
				dscale = dimagediagonalLength / calcLineLength(icroppedCorners);
				dx = dx * dscale;
				dy = -dy * dscale;	
				statusBar.setMessage("DX = " + twoPlaces.format(dx) + "\" DY = " + twoPlaces.format(dy) + "\"                     DX = " + twoPlaces.format(2.54 * dx) + "cm DY = " + twoPlaces.format(2.54 * dy) + "cm");
				int cursorLength = 20;
				cursorLine[0][0][0] = ix - cursorLength / 2;
				cursorLine[0][1][0] = ix + cursorLength / 2;
				cursorLine[0][0][1] = iy;
				cursorLine[0][1][1] = iy;
				cursorLine[1][0][0] = ix;
				cursorLine[1][1][0] = ix;
				cursorLine[1][0][1] = iy - cursorLength / 2;
				cursorLine[1][1][1] = iy + cursorLength / 2;
				validCursor = true;
				validLineCheckBox[lineIndex].setSelected(true);
				resizeMainPanels();
				plotImage();
			}
			return;
		}
		if (lineIndex == 3)
		{
			origin[0] = ix;
			origin[1] = iy;
			int cursorLength = 20;
			originLine[0][0][0] = ix - cursorLength / 2;
			originLine[0][1][0] = ix + cursorLength / 2;
			originLine[0][0][1] = iy;
			originLine[0][1][1] = iy;
			originLine[1][0][0] = ix;
			originLine[1][1][0] = ix;
			originLine[1][0][1] = iy - cursorLength / 2;
			originLine[1][1][1] = iy + cursorLength / 2;
			noOfOriginLines = 2;
			lineEnter = false;
			validLineCheckBox[lineIndex].setSelected(true);
			resizeMainPanels();
			plotImage();
			statusBar.setMessage("Ready");
			validCursor = false;
			return;
		}

		
		int icoord = 1;
		if (lineEnterFirstPt)
		{
			icoord = 0;
		}
		lineCoords[icoord][0] = ix;
		lineCoords[icoord][1] = iy;
		if (icoord == 1) 
		{
			if (lineIndex == 0)
			{
				for (int ii = 0; ii < 2; ++ii)
				{
					for (int ij = 0; ij < 2; ++ij)
						extensionLine[noOfExtensionLines][ii][ij] = lineCoords[ii][ij];
				}
				++noOfExtensionLines;
				if (noOfExtensionLines >= maxNoOfExtensionLines) noOfExtensionLines = maxNoOfExtensionLines - 1;
			}
			if (lineIndex == 1)
			{
				for (int ii = 0; ii < 2; ++ii)
				{
					for (int ij = 0; ij < 2; ++ij)
						unitLine[0][ii][ij] = lineCoords[ii][ij];
				}
				noOfUnitLines = 1;
			}
			if (lineIndex == 2)
			{
				for (int ii = 0; ii < 2; ++ii)
				{
					for (int ij = 0; ij < 2; ++ij)
						measureLine[0][ii][ij] = lineCoords[ii][ij];
				}
				noOfMeasureLines = 1;
			}
			validLine[lineIndex] = true;
			validLineCheckBox[lineIndex].setSelected(true);
			lineEnter = false;
			resizeMainPanels();
			plotImage();
			statusBar.setMessage("Ready");
			if (lineIndex == 1)
				unitLineLength = calcLineLength(unitLine[0]) + 1.0e-10;
			if ((lineIndex == 2) && (unitLineLength > 0.0))
			{
				double dlength = 100.0 * calcLineLength(measureLine[0]) / unitLineLength;
				statusBar.setMessage("Line Length = " + twoPlaces.format(dlength) + " %");
			}
			if ((lineIndex == 2) && (unitLineLength < 0.0))
			{
				double dlength = dimagediagonalLength * calcLineLength(measureLine[0]) / calcLineLength(icroppedCorners);
				statusBar.setMessage("Line Length = " + twoPlaces.format(dlength) + "\"               " + twoPlaces.format(2.54 * dlength) + "cm");
			}
		}
		else
		{
//			messageDialog("Click on End Point for Line");
			statusBar.setMessage("Click on End Point for Line");
			lineEnterFirstPt = false;
			
		}

	}
	protected double calcLineLength(int[][] icoord)
	{
		double dlength = 0.0;
		dlength = ((double) (icoord[1][0] - icoord[0][0])) * ((double) (icoord[1][0] - icoord[0][0]))
			    + ((double) (icoord[1][1] - icoord[0][1])) * ((double) (icoord[1][1] - icoord[0][1]));
		dlength = Math.sqrt(dlength);
		return dlength;
	}
	protected void matchColors(boolean bmatch)
	{
		if (!validTargetColor) return;
		int inumColors = 0;
		for (int ii = 0; ii < maxNoOfMixColors; ++ii)
		{
			if ( colorMixSelection[ii] > -1) inumColors = inumColors + 1;
		}
		if (inumColors < 1) return;
		int[] colors = new int[inumColors];
		int[] colorSliderValue = new int[inumColors];
		inumColors = 0;
		int icolorSelectorFound = 0;
		for (int ii = 0; ii < maxNoOfMixColors; ++ii)
		{
			if ( colorMixSelection[ii] > -1) 
			{
				colors[inumColors] = Integer.valueOf(xpossibleColor[colorMixSelection[ii]], 16).intValue(); 
				colorSliderValue[inumColors] = colorMixValueSlider[ii].getValue();
				icolorSelectorFound = ii;
				inumColors = inumColors + 1;
			}
		}
		imatchColor = colors[0];
		if (inumColors < 2)
		{
			imatchColor = colors[0];
			colorMixValueSlider[icolorSelectorFound].setValue(100);
		}
		else
		{
			if (bmatch)
			{
				ColorMatch cm = new ColorMatch(itargetcolor, inumColors, colors);
				cm.setupOptimization();
				cm.Optimize(0.0001);
				imatchColor = ColorBox.addColors(inumColors, colors, cm.mix);
				inumColors = 0;
				for (int ii = 0; ii < maxNoOfMixColors; ++ii)
				{
					if ( colorMixSelection[ii] > -1) 
					{
						int imix = (int) (100.0 * cm.mix[inumColors]);
						colorMixValueSlider[ii].setValue(imix);
						inumColors = inumColors + 1;
					}
				}
			}
			if (!bmatch)
			{
				double[] mix = new double[inumColors];
				double dsum = 0.0;
				// normalize to 1.0
				for (int ii = 0; ii < inumColors; ++ii)
				{
					mix[ii] = (double) colorSliderValue[ii];
					mix[ii] = mix[ii] / 100.0;
					dsum = mix[ii] + dsum;
				}
				for (int ii = 0; ii < inumColors; ++ii)
				{
					mix[ii] = mix[ii] / dsum;
				}
				imatchColor = ColorBox.addColors(inumColors, colors, mix);
				inumColors = 0;
				for (int ii = 0; ii < maxNoOfMixColors; ++ii)
				{
					if ( colorMixSelection[ii] > -1) 
					{
						int imix = (int) (100.0 * mix[inumColors]);
						colorMixValueSlider[ii].setValue(imix);
						inumColors = inumColors + 1;
					}
				}
			}
		}
		swatchColorLabel[1].setIcon(new ColorBox(imatchColor,ixswatchSize,iyswatchSize, true));
		if (mainTabbedPane.getSelectedIndex() > 0)
			if (imageTabbedPane.getSelectedIndex() == 2)
				imageIconLabel[2].setIcon(new ImageIcon(plotSingleColorImage()));
		double fitMatch = ColorBox.compareColors(itargetcolor, imatchColor);
		fitMatch = 1000.0 * fitMatch;

	}
	protected void satSliderChanged() 
	{
		if (!satSlider[0].getValueIsAdjusting() && !satSlider[1].getValueIsAdjusting()) 
		{
			saturationChange[0] = (int)satSlider[0].getValue();
			saturationChange[1] = (int)satSlider[1].getValue();
			resizeMainPanels();
			plotImage();
		}
	}
	protected void colorSliderChanged() 
	{
		boolean slidersAreAdjusting = false;
		for (int ii = 0; ii < maxNoOfMixColors; ++ii)
		{
			if (colorMixValueSlider[ii].getValueIsAdjusting()) slidersAreAdjusting = true;
		}
		if (!slidersAreAdjusting) 
		{
			if (!colorMatchingOn) matchColors(false);
		}
	}
	protected void levelSliderChanged(int islider) 
	{
		if (singleLevelSlider[islider].getValueIsAdjusting()) return;
		if (inumLevels < 2) return;
		int sliderValue = singleLevelSlider[islider].getValue();
		if (islider > 0)
		{
			if (sliderValue < singleLevelSlider[islider - 1].getValue())
			{
				sliderValue = singleLevelSlider[islider - 1].getValue();
			}
		}
		if (islider <  inumLevels - 2)
		{
			if (sliderValue > singleLevelSlider[islider + 1].getValue())
			{
				sliderValue = singleLevelSlider[islider + 1].getValue();
			}
		}
		xScaleTablebreaks[islider] = sliderValue;
		resizeMainPanels();
		plotImage();
	}
	private void readSettingsTextFieldsFromGui()
	{
		double dminPixelValue = 0.0;
		double dmaxPixelValue = 255.0;
		String sminPixelValue = rGBplotter.minPixelValueTextField.getText();
		String smaxPixelValue = rGBplotter.maxPixelValueTextField.getText();
		if (sminPixelValue != null)
		{
			dminPixelValue = Double.valueOf(sminPixelValue).doubleValue();
		}
		if (smaxPixelValue != null)
		{
			dmaxPixelValue = Double.valueOf(smaxPixelValue).doubleValue();
		}
		rGBplotter.setPixelValueLimits(dminPixelValue,dmaxPixelValue);
	}
	private void setupImageDisplayPanel()
	{
	 	ImageIcon imageIcon = new ImageIcon();
	 	for (int ii = 0; ii < 3; ++ii)
	 	{
	 		imagePanel[ii] = new JPanel();
	 		imagePanel[ii].setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder(""),BorderFactory.createEmptyBorder(5,5,5,5)));
		 	imageIconLabel[ii] = new JLabel(imageIcon);
		 	imagePanel[ii].add(imageIconLabel[ii]);
	 	}
		ThreeToneGuiListeners[] threeToneGuiListen = new ThreeToneGuiListeners[2];
		threeToneGuiListen[0] = new ThreeToneGuiListeners(this, "CL Panel Mouse");
	 	imageIconLabel[0].addMouseListener(threeToneGuiListen[0]);
		threeToneGuiListen[1] = new ThreeToneGuiListeners(this, "BW Panel Mouse");
	 	imageIconLabel[1].addMouseListener(threeToneGuiListen[1]);
	 	return ;
	}
   private  JMenuBar addMenu()
    {
        JMenuBar menuBar = new JMenuBar();
        
        String menuText[] = {"File","Settings","Line Draw","Help"};
        String itemText[][] =
            {
        		{"Open", "Save", "Exit"},
        		{"Show","Hide","Custom Color Palette"},
        		{"Extension", "Unit", "Measure", "Origin", "Clear Extension Lines"},
        		{"Help", "About"}
        	};

        for (int i = 0; i < menuText.length; i++)
        {
            JMenu menu = new JMenu(menuText[i]);
            menuBar.add (menu);
            
            for (int j = 0; j < itemText[i].length; j++)
            {
                JMenuItem item = new JMenuItem(itemText[i][j]);
                menu.add (item);
                item.addActionListener(new ThreeToneGuiListeners(this, menuText[i] + "." +itemText[i][j]));
        		if (itemText[i][j].equals("Open"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_O, ActionEvent.CTRL_MASK));

        		}
        		if (itemText[i][j].equals("Save"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_S, ActionEvent.CTRL_MASK));

        		}
        		if (itemText[i][j].equals("Exit"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_Q, ActionEvent.CTRL_MASK));

        		}
        		if (itemText[i][j].equals("Show"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_S, ActionEvent.ALT_MASK));

        		}
        		if (itemText[i][j].equals("Hide"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_H, ActionEvent.ALT_MASK));

        		}
        		if (itemText[i][j].equals("Extension"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_E, ActionEvent.ALT_MASK));

        		}
        		if (itemText[i][j].equals("Unit"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_U, ActionEvent.ALT_MASK));

        		}
        		if (itemText[i][j].equals("Measure"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_M, ActionEvent.ALT_MASK));

        		}
        		if (itemText[i][j].equals("Origin"))
        		{
        			item.setAccelerator(KeyStroke.getKeyStroke(
        			        KeyEvent.VK_O, ActionEvent.ALT_MASK));

        		}

            }
        }
        
        return menuBar;
    }
	protected void getImageFile()
	{
		FileChooserWrapper fc = new FileChooserWrapper(this);
		File FImageFile = fc.fOpenFile("jpg",false, lastDirectoryPath);
		if (FImageFile == null) return;
     	lastDirectoryPath = FImageFile.getPath();
 		jpegImageIcon = new ImageIcon(FImageFile.getPath());
		resizeMainPanels();
		autoLevelReset = true;
     	plotImage();
		statusBar.setMessage("File Loaded: " + FImageFile.getPath());

	}
	protected BufferedImageWrapper plotBwImage()
	{
	 	BufferedImageWrapper bi2 = null;
	 	bi2 = makeMultiToneImage(bSourceImage, false);
 		if (bi2 == null) return null;
 		BufferedImageWrapper bi3 = null;
 		if (flipSetting)
 		{
 			bi3 = bi2.rotateImage(true).rotateImage(true);
 		}
 		else
 		{
 			bi3 = bi2;
 		}
		bi3.drawGridonImage((Color.MAGENTA).getRGB(), ngrid, ngrid, 3, aspectRatio);
		icroppedCorners = bi3.findCropCorners(aspectRatio);
		if (origin == null)
		{		
			origin = new int[2];
			origin[0] = icroppedCorners[0][0];
			origin[1] = icroppedCorners[1][1];
		}
		if (validLineCheckBox[0].isSelected())
		{
			
			for (int ii = 0; ii < noOfExtensionLines; ++ii)
			{
				int icolor = (Color.GREEN).getRGB();
				boolean iextend = true;
				bi3.drawLineOnImage(extensionLine[ii][0][0], extensionLine[ii][0][1], 
						extensionLine[ii][1][0], extensionLine[ii][1][1], icolor, iextend);
			}
		}
		if (validLineCheckBox[1].isSelected())
		{
			for (int ii = 0; ii < noOfUnitLines; ++ii)
			{
				int icolor = (Color.RED).getRGB();
				boolean iextend = false;
				bi3.drawLineOnImage(unitLine[ii][0][0], unitLine[ii][0][1], 
						unitLine[ii][1][0], unitLine[ii][1][1], icolor, iextend);
			}
		}
		if (validLineCheckBox[2].isSelected())
		{
			for (int ii = 0; ii < noOfMeasureLines; ++ii)
			{
				int icolor = (Color.BLUE).getRGB();
				boolean iextend = false;
				bi3.drawLineOnImage(measureLine[ii][0][0], measureLine[ii][0][1], 
						measureLine[ii][1][0], measureLine[ii][1][1], icolor, iextend);
			}
		}
		if (validLineCheckBox[3].isSelected())
		{
			
			for (int ii = 0; ii < noOfOriginLines; ++ii)
			{
				int icolor = (Color.BLUE).getRGB();
				boolean iextend = false;
				bi3.drawLineOnImage(originLine[ii][0][0], originLine[ii][0][1], 
						originLine[ii][1][0], originLine[ii][1][1], icolor, iextend);
			}
			if (validCursor)
			{
				for (int ii = 0; ii < noOfOriginLines; ++ii)
				{
					int icolor = (Color.RED).getRGB();
					boolean iextend = false;
					bi3.drawLineOnImage(cursorLine[ii][0][0], cursorLine[ii][0][1], 
							cursorLine[ii][1][0], cursorLine[ii][1][1], icolor, iextend);
				}
			}
		}
		return bi3;
	}
	protected BufferedImageWrapper plotSingleColorImage()
	{
	 	BufferedImageWrapper bi2 = null;
	 	bi2 = makeMultiToneImage(bSourceImage, false);
 		if (bi2 == null) return null;
	 	if (imatchColor == 0) return bi2;
		int maxX = bi2.getWidth();
		int maxY = bi2.getHeight();
		Color colorMatch = new Color(imatchColor);
		int iMred = colorMatch.getRed();
		int iMgreen = colorMatch.getGreen();
		int iMblue = colorMatch.getBlue();
		float[] hsbMvals = new float[3];
		Color.RGBtoHSB(iMred, iMgreen, iMblue, hsbMvals);
		
		for (int ii = 0; ii < maxX; ++ii)
		{
			for (int ij = 0; ij < maxY; ++ij)
			{
				itargetcolor = bSourceImage.getAveragePixelColor(ii,ij,1);
				Color	clr  = new Color(itargetcolor);
				int ired = clr.getRed();
				int igreen = clr.getGreen();
				int iblue = clr.getBlue();
				float[] hsbvals = new float[3];
				Color.RGBtoHSB(ired, igreen, iblue, hsbvals);
				double[] cmerror = new double[3];
				cmerror[0] = Math.abs(hsbvals[0] - hsbMvals[0]);
				cmerror[1] = Math.abs(hsbvals[0] - 1.0 - hsbMvals[0]);
				cmerror[2] = Math.abs(hsbvals[0] + 1.0 - hsbMvals[0]);
				double cmerrorMin = cmerror[0];
				if (cmerror[1] < cmerrorMin) cmerrorMin = cmerror[1];
				if (cmerror[2] < cmerrorMin) cmerrorMin = cmerror[2];
				if (cmerrorMin < 0.05)
					bi2.setRGB(ii, ij, itargetcolor);
			}
		}
 		return bi2;
	}
	protected BufferedImageWrapper plotColorImage()
	{
		if (jpegImageIcon == null) return null;
		if (mainTabbedPane.getSelectedIndex() > 0) return null;
// Make a slightly smaller image
		double ratio = 0.90;
		double width = ((double)imageTabbedPane.getWidth()) * ratio;
		double height = ((double)imageTabbedPane.getHeight()) * ratio;
		BufferedImageWrapper bImage = new BufferedImageWrapper(
				jpegImageIcon, 
				(int) width, 
				(int) height);

		double[] satchange = {(double) saturationChange[0], (double) saturationChange[1]};
		bSourceImage = new BufferedImageWrapper(bImage, satchange[0] / 100.0,  satchange[1] / 100.0);

// Plot Color Image
		ImageIcon imageIcon = new ImageIcon(bSourceImage);
	 	imageIconLabel[0].setIcon(imageIcon);
	 	return bSourceImage;
	}
	protected void plotImage()
	{
		if (jpegImageIcon == null) return;
	 	
// Plot Color Image
		ImageIcon imageIcon = new ImageIcon(plotColorImage());
	 	imageIconLabel[0].setIcon(imageIcon);

// Plot B&W Image
	 	if (imageTabbedPane.getSelectedIndex() == 1)
	 	{
	 		imageIcon = new ImageIcon(plotBwImage());
	 		imageIconLabel[1].setIcon(imageIcon);
	 		autoLevelReset = false;
	 	}

// Plot Single Colors Image
	 	if (imageTabbedPane.getSelectedIndex() == 2)
	 		imageIconLabel[2].setIcon(new ImageIcon(plotSingleColorImage()));
	 	
	}
	private BufferedImageWrapper makeMultiToneImage(BufferedImageWrapper bImage, boolean bcolor)
	{
	 	readSettingsTextFieldsFromGui();
	 	PixieImageRGBPlotter pip = new PixieImageRGBPlotter();
	 	try
	 	{
	 		pip.setImages(bImage);
	 	}
        catch (OutOfMemoryError e) 
        {
        	JOptionPane.showMessageDialog(this, "Not Enough Memory\nTry Increasing the Memory of the Java VM");
        	return null;
        }
	 	if (!bcolor) pip.colorToBw();
	 	pip.copyScaling(rGBplotter);
	 	PixieImage pi = pip.getRedImage();
	 	if (pip.getAutoScale()) pip.setPixelValueLimits(pi);
	 	pip.setScaleTable(pip.getRedImage());
	 	if (autoLevelReset)
	 	{
	 		xScaleTablebreaks = findScaleTableBreakPoints(pip, inumLevels);
	 	}
	 	quantizeScaleTable(pip, inumLevels, xScaleTablebreaks);
//	 	pip.quantizeScaleTable(inumLevels);
	 	BufferedImageWrapper bi2 = new BufferedImageWrapper(pip.makeBufferedImage());
	 	return bi2;
	}
	protected void saveFile()
	{
    	if (jpegImageIcon == null) return;
    	int index = imageTabbedPane.getSelectedIndex();
 
    	FileChooserWrapper fc = new FileChooserWrapper(this);
		File FImageFile = fc.fSaveFile("png", lastDirectoryPath);
		if (FImageFile == null) return;
     	lastDirectoryPath = FImageFile.getPath();

		try 
        {
	        if (index == 0) ImageIO.write(plotColorImage(), "png", FImageFile);
	        if (index == 1) ImageIO.write(plotBwImage(), "png", FImageFile);
	        if (index == 2) ImageIO.write(plotSingleColorImage(), "png", FImageFile);
	    } 
        catch (IOException e)
        {
        	System.out.println("Problems writing PNG file");
        }
	}
	protected void messageDialog(String string)
	{
		JOptionPane.showMessageDialog(this, string);
	}
	protected void lineDraw(int index)
	{
    	if (jpegImageIcon == null) 
    	{
     		messageDialog("Need to Load an Image");
       		return;
    	}
    	if (imageTabbedPane.getSelectedIndex() != 1)
    	{
    		messageDialog("Works only on B&W pane");
    		return;
    	}
//		messageDialog("Click on Starting Point for Line");
		if (index < 3)
		{
			statusBar.setMessage("Click on Starting Point for Line");
			lineEnter = true;
			lineIndex = index;
			lineEnterFirstPt = true;
		}
		if (index == 3)
		{
			statusBar.setMessage("Click New Origin");
			lineEnter = true;
			lineIndex = index;
			lineEnterFirstPt = false;
		}
			
	}
	protected void clearExtensionLines()
	{
		noOfExtensionLines = 0;
		validLine[0] = false;
		validLineCheckBox[0].setSelected(false);
		resizeMainPanels();
		plotImage();
	}
	protected JPanel levelSliderSetup()
	{
		JPanel levelSliderPanel = new JPanel();
		levelSliderPanel.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Level Boundaries"),BorderFactory.createEmptyBorder(5,5,5,5)));
		levelSliderPanel.setLayout(new BoxLayout(levelSliderPanel, BoxLayout.Y_AXIS));
		levelSliderPanel.setLayout(new GridLayout(4,1));
		ThreeToneGuiListeners[] levelSliderListen = new ThreeToneGuiListeners[5];
		for (int ii = 0; ii < 4; ++ii)
		{
			singleLevelSlider[ii] = new JSlider(JSlider.HORIZONTAL, 0, 256, 0);
			levelSliderListen[ii] = new ThreeToneGuiListeners(this, "Level " + ii + " Slider Changed");
			singleLevelSlider[ii].addChangeListener(levelSliderListen[ii]);

//			Turn on labels at major tick marks.
			singleLevelSlider[ii].setMajorTickSpacing(64);
			singleLevelSlider[ii].setMinorTickSpacing(64);
			singleLevelSlider[ii].setPaintTicks(true);
			singleLevelSlider[ii].setPaintLabels(true);
			singleLevelSlider[ii].setSnapToTicks(false);
			singleLevelSlider[ii].setVisible(false);
			
			
			levelSliderPanel.add(singleLevelSlider[ii]);

		}
		return levelSliderPanel;
	}
	protected int[] findScaleTableBreakPoints(PixieImageRGBPlotter pip, int steps)
	{
		int[] xbreak = new int[steps];
		int[] ybreak = new int[steps];
		if (steps < 1) return null;
		if (steps  == 1) 
		{
			xbreak[steps - 1] = 255;
			return xbreak;
		}
		int icount = 0;
		for (int ii = 0; ii < steps; ++ii)
		{
			double yb = 255.0 * ((double) (ii + 1)) / ((double) steps);
			ybreak[ii] = (int) yb;
			boolean ibreakFound = false;
			while (!ibreakFound && (icount < 256))
			{
				if (pip.scaleTable[icount] > ybreak[ii])
				{
					ibreakFound = true;
					xbreak[ii] = icount;
					icount = icount + 1;
				}
				else
				{
					icount = icount + 1;
				}
			}
		}
		xbreak[steps - 1] = 255;
		return xbreak;
	}
	protected void quantizeScaleTable(PixieImageRGBPlotter pip, int steps, int[] xbreak)
	{
		if (steps < 2) return;
		ignoreLevelChange = true;
		int ystep = 255 / (2 * steps);
		int istep = 255 / steps;
		int icount = 0;
		for (int ii = 0; ii < 4; ++ii)
		{
			singleLevelSlider[ii].setVisible(false);
		}
		for (int ii = 0; ii < steps; ++ii)
		{
			for ( int ij = icount; ij <= xbreak[ii]; ++ij)
			{
				pip.scaleTable[ij] = ystep;
			}
			icount = xbreak[ii] + 1;
			ystep = ystep + istep;
			if (ii < steps - 1) 
			{
				singleLevelSlider[ii].setVisible(true);
				singleLevelSlider[ii].setValue(xbreak[ii]);
			}
		}
		ignoreLevelChange = false;
	}

	/**
	 * @param args
	 * @throws SimpleXmlException 
	 * @throws SimpleXmlException 
	 */
	public static void main(String[] args) throws SimpleXmlException 
	{
       try 
        {
            UIManager.setLookAndFeel(
            UIManager.getCrossPlatformLookAndFeelClassName());
        } catch (Exception e) {}
        
       //Create the top-level container and add contents to it.
        ThreeToneGui app = new ThreeToneGui();
        JFrame frame = new JFrame("Tommy Three Tone " + app.version);
        frame.setJMenuBar(app.addMenu());
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		URL resourceURL = loader.getResource("tommyTruck.jpg");
		ImageIcon iconImage = new ImageIcon(resourceURL);
        frame.setIconImage(iconImage.getImage());

//        Component contents = app.createComponents();
        mainFrameContents = app.createComponents();
        frame.getContentPane().add(mainFrameContents, BorderLayout.CENTER);
        frame.getContentPane().add(statusBar, java.awt.BorderLayout.SOUTH);  
        
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
        frame.setExtendedState(frame.getExtendedState()|JFrame.MAXIMIZED_BOTH);
        app.loadSplashScreen();
 	}
}
